package com.htt.bms.security;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.htt.bms.common.WrapMapper;
import com.htt.bms.common.Wrapper;
import com.htt.bms.common.constant.Enums;
import com.htt.bms.domain.sys.SysUser;
import com.htt.bms.domain.sys.vo.SysUserVo;
import com.htt.bms.utils.JwtTokenUtils;
import com.htt.bms.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collection;

/**
 * @author 侯婷婷
 * @version 1.0
 * @description 用户认证，配置拦截器，用账号密码登录
 */
@Slf4j
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    /**
     * 设置过滤url
     * @param authenticationManager
     */
    public JWTAuthenticationFilter(AuthenticationManager authenticationManager){
        this.authenticationManager = authenticationManager;
        //设置过滤url，默认login，当url为 /user/login时进行过滤
        log.info("/user/login已被拦截...");
        super.setFilterProcessesUrl("/user/login");
    }

    /**
     * 获取登录信息，实现身份认证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        SysUser user = null;
        try {
            user = new ObjectMapper().readValue(request.getInputStream(),SysUser.class);
        } catch (IOException e) {
            log.error("=============获取登录信息异常：{}",e);
        }

        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword()));
    }

    /**
     * 验证成功后调用的方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        SecurityUtils user = (SecurityUtils) authResult.getPrincipal();
        String role = "";

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            //只有一个role
            role = authority.getAuthority();
        }

        //创建token
        String token = JwtTokenUtils.createToken(
                SysUser.builder()
                        .id(user.getId())
                        .username(user.getUsername())
                        .password(user.getPassword())
                        .type(user.getType())
                        .orgId(user.getOrgId())
                        .orgIds(user.getOrgIds())
                        .role(role)
                        .build(),false
        );

        //创建token成功后，请求格式应该是 ‘Bearer token’
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(JSONObject.toJSONString(WrapMapper.success().result(
                SysUserVo.builder()
                        .username(user.getUsername())
                        .nickname(user.getNickname())
                        .token(token)
                        .loginTime(LocalDateTime.now()).build()
        )));
    }

    /**
     * 登录失败后调用方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        log.info("=============登录失败：{}=============",failed.getMessage());
        Wrapper result = null;
        if (failed instanceof UsernameNotFoundException){
            result = WrapMapper.error(Enums.AUTH_NONEXISTENT.getKey(), Enums.AUTH_NONEXISTENT.getValue());
        } else if (failed instanceof BadCredentialsException) {
            result = WrapMapper.wrap(Enums.AUTH_NO_TOKEN.getKey(), Enums.AUTH_NO_TOKEN.getValue());
        } else if (failed instanceof InternalAuthenticationServiceException) {
            String message = failed.getMessage() != null ? failed.getMessage() : Wrapper.ERROR_MESSAGE;
            result = WrapMapper.error(Enums.AUTH_NO_ACCESS.getKey(), message);
        }

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(JSONObject.toJSONString(result));
    }
}
